package com.gitee.fastmybatis.core.query;

import com.gitee.fastmybatis.core.SqlConsts;
import com.gitee.fastmybatis.core.query.expression.BetweenValue;
import com.gitee.fastmybatis.core.query.expression.Expression;
import com.gitee.fastmybatis.core.query.expression.ExpressionJoinable;
import com.gitee.fastmybatis.core.query.expression.Expressions;
import com.gitee.fastmybatis.core.query.expression.SubExpression;
import com.gitee.fastmybatis.core.query.expression.ValueConvert;
import com.gitee.fastmybatis.core.query.expression.ValueExpression;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;

/**
 * 条件类，存放各种查询条件
 *
 * @author thc
 */
public class Condition {

    private static final String DEFAULT_SQL_INJECT_REGEX = "([';])+|(--)+";

    private static final String DEFAULT_ARG_PLACEHOLDER = "?";

    private String argPlaceholder = DEFAULT_ARG_PLACEHOLDER;

    private String sqlInjectRegex = DEFAULT_SQL_INJECT_REGEX;

    /**
     * 条件表达式
     */
    protected final List<Expression> expressions = new ArrayList<>(4);

    public void setArgPlaceholder(String argPlaceholder) {
        this.argPlaceholder = argPlaceholder;
    }

    public void setSqlInjectRegex(String sqlInjectRegex) {
        this.sqlInjectRegex = sqlInjectRegex;
    }

    public String getArgPlaceholder() {
        return argPlaceholder;
    }

    public String getSqlInjectRegex() {
        return sqlInjectRegex;
    }

    // ------------ 基本条件 ------------

    /**
     * 添加等于条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition eq(String columnName, Object value) {
        this.addExpression(Expressions.eq(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加等于条件
     * <pre>
     *     query.eq(StringUtils.hasText(name), "name", name);
     *     等同于：
     *     if (StringUtils.hasText(name)) {
     *         query.eq("name", name);
     *     }
     * </pre>
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition eq(boolean expression, String columnName, Object value) {
        if (expression) {
            eq(columnName, value);
        }
        return this;
    }

    /**
     * 添加不等于条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notEq(String columnName, Object value) {
        this.addExpression(Expressions.notEq(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加不等于条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notEq(boolean expression, String columnName, Object value) {
        if (expression) {
            notEq(columnName, value);
        }
        return this;
    }

    /**
     * 添加大于条件,>
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition gt(String columnName, Object value) {
        this.addExpression(Expressions.gt(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加大于条件,>
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition gt(boolean expression, String columnName, Object value) {
        if (expression) {
            gt(columnName, value);
        }
        return this;
    }

    /**
     * 添加大于等于条件,>=
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition ge(String columnName, Object value) {
        this.addExpression(Expressions.ge(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加大于等于条件,>=
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition ge(boolean expression, String columnName, Object value) {
        if (expression) {
            ge(columnName, value);
        }
        return this;
    }

    /**
     * 添加小于条件,<
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition lt(String columnName, Object value) {
        this.addExpression(Expressions.lt(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加小于条件,<
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition lt(boolean expression, String columnName, Object value) {
        if (expression) {
            lt(columnName, value);
        }
        return this;
    }

    /**
     * 小于等于,<=
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition le(String columnName, Object value) {
        this.addExpression(Expressions.le(columnName, value));
        return this;
    }

    /**
     * 根据表达式小于等于条件,<=
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition le(boolean expression, String columnName, Object value) {
        if (expression) {
            le(columnName, value);
        }
        return this;
    }

    /**
     * 添加两边模糊查询条件，两边模糊匹配，即name like '%value%'
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     * @see #likeLeft(String, String) 左边模糊匹配
     * @see #likeRight(String, String) 右边模糊匹配
     */
    public Condition like(String columnName, String value) {
        this.addExpression(Expressions.like(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加两边模糊查询条件，两边模糊匹配，即name like '%value%'
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     * @see #likeLeft(boolean, String, String) 左模糊
     * @see #likeRight(boolean, String, String) 右模糊
     */
    public Condition like(boolean expression, String columnName, String value) {
        if (expression) {
            like(columnName, value);
        }
        return this;
    }

    /**
     * 添加左模糊查询条件，左边模糊匹配，即name like '%value'
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition likeLeft(String columnName, String value) {
        this.addExpression(Expressions.likeLeft(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加左模糊查询条件，左边模糊匹配，即name like '%value'
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition likeLeft(boolean expression, String columnName, String value) {
        if (expression) {
            likeLeft(columnName, value);
        }
        return this;
    }

    /**
     * 添加右模糊查询条件，右边模糊匹配，即name like 'value%'。mysql推荐用这种
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition likeRight(String columnName, String value) {
        this.addExpression(Expressions.likeRight(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加右模糊查询条件，右边模糊匹配，即name like 'value%'。mysql推荐用这种
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition likeRight(boolean expression, String columnName, String value) {
        if (expression) {
            likeRight(columnName, value);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition in(String columnName, Collection<?> value) {
        this.addExpression(Expressions.in(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition in(boolean expression, String columnName, Collection<?> value) {
        if (expression) {
            in(columnName, value);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换
     * @return 返回Query对象
     */
    public <T> Condition in(String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        this.addExpression(Expressions.in(columnName, value, valueConvert));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换
     * @return 返回Query对象
     */
    public <T> Condition in(boolean expression, String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        if (expression) {
            in(columnName, value, valueConvert);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition in(String columnName, Object[] value) {
        this.addExpression(Expressions.in(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition in(boolean expression, String columnName, Object[] value) {
        if (expression) {
            in(columnName, value);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notIn(String columnName, Collection<?> value) {
        this.addExpression(Expressions.notIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notIn(boolean expression, String columnName, Collection<?> value) {
        if (expression) {
            notIn(columnName, value);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换器
     * @return 返回Query对象
     */
    public <T> Condition notIn(String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        this.addExpression(Expressions.notIn(columnName, value, valueConvert));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换器
     * @return 返回Query对象
     */
    public <T> Condition notIn(boolean expression, String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        if (expression) {
            notIn(columnName, value, valueConvert);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notIn(String columnName, Object[] value) {
        this.addExpression(Expressions.notIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition notIn(boolean expression, String columnName, Object[] value) {
        if (expression) {
            notIn(columnName, value);
        }
        return this;
    }

    private Condition doBetween(String columnName, Object value) {
        addExpression(Expressions.between(columnName, value));
        return this;
    }

    /**
     * 添加between条件
     *
     * @param columnName 数据库字段名
     * @param startValue 起始值
     * @param endValue   结束值
     * @return 返回Query对象
     */
    public Condition between(String columnName, Object startValue, Object endValue) {
        between(columnName, new BetweenValue(startValue, endValue));
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param startValue 起始值
     * @param endValue   结束值
     * @return 返回Query对象
     */
    public Condition between(boolean expression, String columnName, Object startValue, Object endValue) {
        if (expression) {
            between(columnName, startValue, endValue);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * Object[] arr = new Object[]{1, 100};
     * query.between(arr);
     * }
     * </pre>
     *
     * @param values 存放起始值、结束值，只能存放2个值，values[0]表示开始值，value[1]表示结束值
     * @return 返回Query对象
     */
    public Condition between(String columnName, Object[] values) {
        doBetween(columnName, values);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param values     存放起始值、结束值，只能存放2个值，values[0]表示开始值，value[1]表示结束值
     * @return 返回Query对象
     */
    public Condition between(boolean expression, String columnName, Object[] values) {
        if (expression) {
            between(columnName, values);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * query.between(Arrays.asList(1, 100));
     * }
     * </pre>
     *
     * @param values 存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition between(String columnName, List<?> values) {
        doBetween(columnName, values);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param values     存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition between(boolean expression, String columnName, List<?> values) {
        if (expression) {
            between(columnName, values);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * query.between(new BetweenValue(1, 100));
     * }
     * </pre>
     *
     * @param betweenValue 起始值、结束值包装对象
     * @return 返回Query对象
     */
    public Condition between(String columnName, BetweenValue betweenValue) {
        doBetween(columnName, betweenValue);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param betweenValue 存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition between(boolean expression, String columnName, BetweenValue betweenValue) {
        if (expression) {
            between(columnName, betweenValue);
        }
        return this;
    }

    /**
     * 添加自定义sql条件
     *
     * @param sql 自定义sql
     * @return 返回Query对象
     */
    public Condition sql(String sql) {
        this.addExpression(Expressions.sql(sql));
        return this;
    }

    /**
     * 根据表达式添加自定义sql条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param sql        自定义sql
     * @return 返回Query对象
     */
    public Condition sql(boolean expression, String sql) {
        if (expression) {
            sql(sql);
        }
        return this;
    }

    /**
     * 添加自定义sql条件
     *
     * @param sqlFormat SQL模板，参数值使用?代替，如：<code>username = ? and nickname like '%?%'</code>
     * @param args      参数
     * @return 返回Query对象
     */
    public Condition sql(String sqlFormat, Object... args) {
        String sql = formatSql(sqlFormat, args);
        this.addExpression(Expressions.sql(sql));
        return this;
    }

    /**
     * 根据表达式添加自定义sql条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param sqlFormat  SQL模板，参数值使用?代替，如：<code>username = ? and nickname like '%?%'</code>
     * @param args       参数
     * @return 返回Query对象
     */
    public Condition sql(boolean expression, String sqlFormat, Object... args) {
        if (expression) {
            sql(sqlFormat, args);
        }
        return this;
    }

    /**
     * 添加字段不为null的条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition notNull(String column) {
        return this.sql(column + " IS NOT NULL");
    }

    /**
     * 根据表达式添加字段不为null的条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition notNull(boolean expression, String column) {
        if (expression) {
            notNull(column);
        }
        return this;
    }

    /**
     * 添加字段是null的条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition isNull(String column) {
        return this.sql(column + " IS NULL");
    }

    /**
     * 根据表达式添加字段是null的条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition isNull(boolean expression, String column) {
        if (expression) {
            isNull(column);
        }
        return this;
    }

    /**
     * 添加不为空字符串条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition notEmpty(String column) {
        return this.sql(column + " IS NOT NULL AND " + column + " <> '' ");
    }

    /**
     * 根据表达式添加不为空字符串条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition notEmpty(boolean expression, String column) {
        if (expression) {
            notEmpty(column);
        }
        return this;
    }

    /**
     * 添加空字段条件，null或者空字符串
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition isEmpty(String column) {
        return this.sql(column + " IS NULL OR " + column + " = '' ");
    }

    /**
     * 根据表达式添加空字段条件，null或者空字符串
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition isEmpty(boolean expression, String column) {
        if (expression) {
            isEmpty(column);
        }
        return this;
    }

    /**
     * 添加1=2条件
     *
     * @return 返回Query对象
     */
    public Condition oneEqTwo() {
        return this.sql("1=2");
    }

    /**
     * 根据表达式添加1=2条件
     *
     * @param expression 表达式，当为true时添加条件
     * @return 返回Query对象
     */
    public Condition oneEqTwo(boolean expression) {
        if (expression) {
            oneEqTwo();
        }
        return this;
    }

    /**
     * 使用key/value进行多个等于的比对,相当于多个eq的效果
     *
     * @param map 键值对
     * @return 返回Query对象
     */
    public Condition allEq(LinkedHashMap<String, Object> map) {
        Set<String> keys = map.keySet();
        for (String columnName : keys) {
            this.eq(columnName, map.get(columnName));
        }
        return this;
    }

    /**
     * 根据表达式添加使用key/value进行多个等于的比对,相当于多个eq的效果
     *
     * @param expression 表达式，当为true时添加条件
     * @param map        键值对
     * @return 返回Query对象
     */
    public Condition allEq(boolean expression, LinkedHashMap<String, Object> map) {
        if (expression) {
            allEq(map);
        }
        return this;
    }

    /**
     * 添加等于条件or
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orEq(String columnName, Object value) {
        addExpression(new ValueExpression(Joint.OR.getJoint(), columnName, Operator.eq.getOperator(), value));
        return this;
    }

    /**
     * 根据表达式添加等于条件
     * <pre>
     *     query.orEq(StringUtils.hasText(name), "name", name);
     *     等同于：
     *     if (StringUtils.hasText(name)) {
     *         query.orEq("name", name);
     *     }
     * </pre>
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orEq(boolean expression, String columnName, Object value) {
        if (expression) {
            orEq(columnName, value);
        }
        return this;
    }

    /**
     * 添加不等于条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotEq(String columnName, Object value) {
        this.addExpression(Expressions.orNotEq(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加不等于条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotEq(boolean expression, String columnName, Object value) {
        if (expression) {
            orNotEq(columnName, value);
        }
        return this;
    }

    /**
     * 添加大于条件,>
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orGt(String columnName, Object value) {
        this.addExpression(Expressions.orGt(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加大于条件,>
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orGt(boolean expression, String columnName, Object value) {
        if (expression) {
            orGt(columnName, value);
        }
        return this;
    }

    /**
     * 添加大于等于条件,>=
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orGe(String columnName, Object value) {
        this.addExpression(Expressions.orGe(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加大于等于条件,>=
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orGe(boolean expression, String columnName, Object value) {
        if (expression) {
            orGe(columnName, value);
        }
        return this;
    }

    /**
     * 添加小于条件,<
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orLt(String columnName, Object value) {
        this.addExpression(Expressions.orLt(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加小于条件,<
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orLt(boolean expression, String columnName, Object value) {
        if (expression) {
            orLt(columnName, value);
        }
        return this;
    }

    /**
     * 小于等于,<=
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orLe(String columnName, Object value) {
        this.addExpression(Expressions.orLe(columnName, value));
        return this;
    }

    /**
     * 根据表达式小于等于条件,<=
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orLe(boolean expression, String columnName, Object value) {
        if (expression) {
            orLe(columnName, value);
        }
        return this;
    }

    /**
     * 添加两边模糊查询条件，两边模糊匹配，即name orLike '%value%'
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     * @see #orLikeLeft(String, String) 左边模糊匹配
     * @see #orLikeRight(String, String) 右边模糊匹配
     */
    public Condition orLike(String columnName, String value) {
        this.addExpression(Expressions.orLike(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加两边模糊查询条件，两边模糊匹配，即name orLike '%value%'
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     * @see #orLikeLeft(boolean, String, String) 左模糊
     * @see #orLikeRight(boolean, String, String) 右模糊
     */
    public Condition orLike(boolean expression, String columnName, String value) {
        if (expression) {
            orLike(columnName, value);
        }
        return this;
    }

    /**
     * 添加左模糊查询条件，左边模糊匹配，即name orLike '%value'
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition orLikeLeft(String columnName, String value) {
        this.addExpression(Expressions.orLikeLeft(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加左模糊查询条件，左边模糊匹配，即name orLike '%value'
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition orLikeLeft(boolean expression, String columnName, String value) {
        if (expression) {
            orLikeLeft(columnName, value);
        }
        return this;
    }

    /**
     * 添加右模糊查询条件，右边模糊匹配，即name orLike 'value%'。mysql推荐用这种
     *
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition orLikeRight(String columnName, String value) {
        this.addExpression(Expressions.orLikeRight(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加右模糊查询条件，右边模糊匹配，即name orLike 'value%'。mysql推荐用这种
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值,不需要加%
     * @return 返回Query对象
     */
    public Condition orLikeRight(boolean expression, String columnName, String value) {
        if (expression) {
            orLikeRight(columnName, value);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orIn(String columnName, Collection<?> value) {
        this.addExpression(Expressions.orIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orIn(boolean expression, String columnName, Collection<?> value) {
        if (expression) {
            orIn(columnName, value);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换
     * @return 返回Query对象
     */
    public <T> Condition orIn(String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        this.addExpression(Expressions.orIn(columnName, value, valueConvert));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换
     * @return 返回Query对象
     */
    public <T> Condition orIn(boolean expression, String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        if (expression) {
            orIn(columnName, value, valueConvert);
        }
        return this;
    }

    /**
     * 添加IN条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orIn(String columnName, Object[] value) {
        this.addExpression(Expressions.orIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加IN条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orIn(boolean expression, String columnName, Object[] value) {
        if (expression) {
            orIn(columnName, value);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotIn(String columnName, Collection<?> value) {
        this.addExpression(Expressions.orNotIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotIn(boolean expression, String columnName, Collection<?> value) {
        if (expression) {
            orNotIn(columnName, value);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换器
     * @return 返回Query对象
     */
    public <T> Condition orNotIn(String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        this.addExpression(Expressions.orNotIn(columnName, value, valueConvert));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param value        值
     * @param valueConvert 转换器
     * @return 返回Query对象
     */
    public <T> Condition orNotIn(boolean expression, String columnName, Collection<T> value, ValueConvert<T> valueConvert) {
        if (expression) {
            orNotIn(columnName, value, valueConvert);
        }
        return this;
    }

    /**
     * 添加not in条件
     *
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotIn(String columnName, Object[] value) {
        this.addExpression(Expressions.orNotIn(columnName, value));
        return this;
    }

    /**
     * 根据表达式添加not in条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param value      值
     * @return 返回Query对象
     */
    public Condition orNotIn(boolean expression, String columnName, Object[] value) {
        if (expression) {
            orNotIn(columnName, value);
        }
        return this;
    }

    private Condition doOrBetween(String columnName, Object value) {
        addExpression(Expressions.orBetween(columnName, value));
        return this;
    }

    /**
     * 添加between条件
     *
     * @param columnName 数据库字段名
     * @param startValue 起始值
     * @param endValue   结束值
     * @return 返回Query对象
     */
    public Condition orBetween(String columnName, Object startValue, Object endValue) {
        orBetween(columnName, new BetweenValue(startValue, endValue));
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param startValue 起始值
     * @param endValue   结束值
     * @return 返回Query对象
     */
    public Condition orBetween(boolean expression, String columnName, Object startValue, Object endValue) {
        if (expression) {
            orBetween(columnName, startValue, endValue);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * Object[] arr = new Object[]{1, 100};
     * query.orBetween(arr);
     * }
     * </pre>
     *
     * @param values 存放起始值、结束值，只能存放2个值，values[0]表示开始值，value[1]表示结束值
     * @return 返回Query对象
     */
    public Condition orBetween(String columnName, Object[] values) {
        doOrBetween(columnName, values);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param values     存放起始值、结束值，只能存放2个值，values[0]表示开始值，value[1]表示结束值
     * @return 返回Query对象
     */
    public Condition orBetween(boolean expression, String columnName, Object[] values) {
        if (expression) {
            orBetween(columnName, values);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * query.orBetween(Arrays.asList(1, 100));
     * }
     * </pre>
     *
     * @param values 存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition orBetween(String columnName, List<?> values) {
        doOrBetween(columnName, values);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param columnName 数据库字段名
     * @param values     存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition orBetween(boolean expression, String columnName, List<?> values) {
        if (expression) {
            orBetween(columnName, values);
        }
        return this;
    }

    /**
     * 添加between条件
     * <pre>
     * {@literal
     * query.orBetween(new BetweenValue(1, 100));
     * }
     * </pre>
     *
     * @param betweenValue 起始值、结束值包装对象
     * @return 返回Query对象
     */
    public Condition orBetween(String columnName, BetweenValue betweenValue) {
        doOrBetween(columnName, betweenValue);
        return this;
    }

    /**
     * 添加between条件
     *
     * @param expression   表达式，当为true时添加条件
     * @param columnName   数据库字段名
     * @param betweenValue 存放起始值、结束值，只能存放2个值
     * @return 返回Query对象
     */
    public Condition orBetween(boolean expression, String columnName, BetweenValue betweenValue) {
        if (expression) {
            orBetween(columnName, betweenValue);
        }
        return this;
    }

    /**
     * 添加自定义sql条件
     *
     * @param orSql 自定义sql
     * @return 返回Query对象
     */
    public Condition orSql(String orSql) {
        this.addExpression(Expressions.orSql(orSql));
        return this;
    }

    /**
     * 根据表达式添加自定义sql条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param orSql      自定义sql
     * @return 返回Query对象
     */
    public Condition orSql(boolean expression, String orSql) {
        if (expression) {
            orSql(orSql);
        }
        return this;
    }

    /**
     * 添加自定义sql条件
     *
     * @param sqlFormat SQL模板，参数值使用?代替，如：<code>username = ? and nickname orLike '%?%'</code>
     * @param args      参数
     * @return 返回Query对象
     */
    public Condition orSql(String sqlFormat, Object... args) {
        String sql = formatSql(sqlFormat, args);
        this.addExpression(Expressions.orSql(sql));
        return this;
    }

    private String formatSql(String sqlFormat, Object... args) {
        if (args != null && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (arg instanceof CharSequence) {
                    args[i] = arg.toString().replaceAll(sqlInjectRegex, SqlConsts.EMPTY);
                }
            }
            for (Object arg : args) {
                sqlFormat = sqlFormat.replaceFirst(String.format("\\%s", this.argPlaceholder), String.valueOf(arg));
            }
        }
        return sqlFormat;
    }

    /**
     * 根据表达式添加自定义sql条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param sqlFormat  SQL模板，参数值使用?代替，如：<code>username = ? and nickname orLike '%?%'</code>
     * @param args       参数
     * @return 返回Query对象
     */
    public Condition orSql(boolean expression, String sqlFormat, Object... args) {
        if (expression) {
            orSql(sqlFormat, args);
        }
        return this;
    }

    /**
     * 添加字段不为null的条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition orNotNull(String column) {
        return this.orSql(column + " IS NOT NULL");
    }

    /**
     * 根据表达式添加字段不为null的条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition orNotNull(boolean expression, String column) {
        if (expression) {
            orNotNull(column);
        }
        return this;
    }

    /**
     * 添加字段是null的条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition orIsNull(String column) {
        return this.orSql(column + " IS NULL");
    }

    /**
     * 根据表达式添加字段是null的条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition orIsNull(boolean expression, String column) {
        if (expression) {
            orIsNull(column);
        }
        return this;
    }

    /**
     * 添加不为空字符串条件
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition orNotEmpty(String column) {
        return this.orSql(column + " IS NOT NULL AND " + column + " <> '' ");
    }

    /**
     * 根据表达式添加不为空字符串条件
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition orNotEmpty(boolean expression, String column) {
        if (expression) {
            orNotEmpty(column);
        }
        return this;
    }

    /**
     * 添加空字段条件，null或者空字符串
     *
     * @param column 数据库字段名
     * @return 返回Query对象
     */
    public Condition orIsEmpty(String column) {
        return this.orSql(column + " IS NULL OR " + column + " = '' ");
    }

    /**
     * 根据表达式添加空字段条件，null或者空字符串
     *
     * @param expression 表达式，当为true时添加条件
     * @param column     数据库字段名
     * @return 返回Query对象
     */
    public Condition orIsEmpty(boolean expression, String column) {
        if (expression) {
            orIsEmpty(column);
        }
        return this;
    }


    // ------------ 基本条件 end------------

    public Condition addExpression(Expression expression) {
        if (expression instanceof ExpressionJoinable) {
            throw new IllegalArgumentException("Where不支持Join表达式");
        } else {
            expressions.add(expression);
        }
        return this;
    }

    public List<Expression> getExpressions() {
        return ExpressionSortUtil.sort(expressions);
    }

    protected Condition subCondition(Joint joint, Condition condition) {
        this.addExpression(new SubExpression(joint.getJoint(), condition));
        return this;
    }
}
